﻿using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Simp.CodeModel
{
	/// <summary>Holds a call to an unbound, or unresolved, function.</summary>
	[ImmutableObjectAttribute(true)]
	public class UnboundFunctionCalling : ParameterLessFunctionBase, IFunctionSerializer
	{
		private readonly string _functionName;
		private readonly IFunctionParameterBinding[] _parameters;
		private readonly bool _isParameter;

		/// <summary>Constructor taking the function's name and its parameters.</summary>
		/// <param name="functionName"/>
		/// <param name="isParameter"/>
		/// <param name="parameters"/>
		public UnboundFunctionCalling(string functionName, bool isParameter, params IFunctionParameterBinding[] parameters)
		{
			if (string.IsNullOrEmpty(functionName))
			{
				throw new ArgumentNullException("functionName");
			}
			if (parameters == null)
			{
				throw new ArgumentNullException("parameters");
			}
			_functionName = functionName;
			_isParameter = isParameter;
			_parameters = parameters;
		}

		#region IFunctionSerializer Members
		void IFunctionSerializer.Serialize(AppendOnlyStringBuilder builder, params IFunction[] parameters)
		{
			builder.Append("[");
			builder.Append(FunctionName);
			builder.Append("]");
			if (FunctionParameters.Length > 0)
			{
				builder.Append("(");
				for (int i = 0; i != FunctionParameters.Length; ++i)
				{
					IFunctionParameterBinding binding = FunctionParameters[i];

					if (!string.IsNullOrEmpty(binding.Name))
					{
						builder.Append(binding.Name);
						builder.Append(" = ");
					}
					FunctionHelper.Serialize(binding.Value, builder);
					if (i != FunctionParameters.Length - 1)
					{
						builder.Append(", ");
					}
				}
				builder.Append(")");
			}
		}
		#endregion

		/// <summary>Returns <c>true</c> iif this represents a function parameter.</summary>
		/// <remarks>
		/// Behaviours are different when an unbound function is a parameter since it should remain unbound until parameters
		/// are applied on the owning function.
		/// </remarks>
		public bool IsParameter
		{
			get { return _isParameter; }
		}

		/// <summary>Parameters to apply on the function.</summary>
		public IFunctionParameterBinding[] FunctionParameters
		{
			get { return _parameters; }
		}

		/// <summary>Name of the function to call (and to resolve).</summary>
		public override string FunctionName
		{
			get { return _functionName; }
		}

		/// <summary>Accepts the visitor and let it visit each parameters.</summary>
		/// <param name="visitor"></param>
		/// <returns></returns>
		public override IFunction AcceptVisitor(IFunctionVisitor visitor)
		{
			bool hasChanged = false;
			IFunctionParameterBinding[] newBindings = new IFunctionParameterBinding[FunctionParameters.Length];

			for (int i = 0; i != newBindings.Length; ++i)
			{
				var param = FunctionParameters[i];
				var newValue = visitor.Visit(param.Value);

				if (newValue != null)
				{
					newBindings[i] = FunctionHelper.FunctionParameterBindingFactory.Build(param.Name, newValue);
					hasChanged = true;
				}
				else
				{
					newBindings[i] = param;
				}
			}

			if (hasChanged)
			{
				return new UnboundFunctionCalling(_functionName, _isParameter, newBindings);
			}
			else
			{	//	Nothing has changed
				return null;
			}
		}

		/// <summary>Can't simplify this function since the function is unresolved, hence unknown.</summary>
		/// <param name="simplificationMode"/>
		/// <returns></returns>
		protected override IFunction Simplify(SimplificationMode simplificationMode)
		{
			bool hasChanged = false;
			IFunctionParameterBinding[] newBindings = new IFunctionParameterBinding[FunctionParameters.Length];

			for (int i = 0; i != newBindings.Length; ++i)
			{
				var param = FunctionParameters[i];
				var newValue = param.Value.Simplify(simplificationMode);

				if (newValue != null)
				{
					newBindings[i] = FunctionHelper.FunctionParameterBindingFactory.Build(param.Name, newValue);
					hasChanged = true;
				}
				else
				{
					newBindings[i] = param;
				}
			}

			if (hasChanged)
			{
				return new UnboundFunctionCalling(_functionName, _isParameter, newBindings);
			}
			else
			{	//	Nothing has changed
				return null;
			}
		}
	}
}